#
# Copyright 2009 Red Hat, Inc. and/or its affiliates.
#
# Licensed to you under the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the files README and
# LICENSE_GPL_v2 which accompany this distribution.
#

import xmlrpclib
import time

import uuid

BLANK_UUID = '00000000-0000-0000-0000-000000000000'

NFS_SERVER_TYPE = 1

DATA_CLASS = 1
ISO_CLASS = 2
BACKUP_DOMAIN = 3

# Volume Types
UNKNOWN_VOL = 0
PREALLOCATED_VOL = 1
SPARSE_VOL = 2

# Volume Format
UNKNOWN_FORMAT = 3
COW_FORMAT = 4
RAW_FORMAT = 5

# Volume Role
SHARED_VOL = 6
INTERNAL_VOL = 7
LEAF_VOL = 8

# Image Operation
COPYOP = 1
MOVEOP = 2

# Volume Legality
ILLEGAL_VOL = "ILLEGAL"
LEGAL_VOL = "LEGAL"

class TaskError(Exception):
    def __init__(self, resp):
        self.resp = resp

    def __str__(self):
        return str(self.resp)

class TaskNotFinished(TaskError):
    pass

class TaskFailed(TaskError):
    pass

#TODO : use functools when moving to 2.6
class partial(object):

    #__doc__ = property(lambda self: self._method.__doc__)

    def __init__(self, method, *args, **kwargs):
        self._args = args
        self._kwargs = kwargs
        self._method = method

        if hasattr(method, "__doc__"):
            self.__doc__ = method.__doc__

    def __call__(self, *args, **kwargs):
        kwargs.update(self._kwargs)
        return self._method(*(self._args + args), **kwargs)

class VDSClient(object):
    '''Wraps the VDS client interface and provides most (if not all) basic
    primitives.'''

    def __init__(self):
        self.debug = 0

    def __getattr__(self, name):
        wrappedAttr = partial(self._callWrapper, name)
        wrappedAttr.__doc__ = self.engine.system.methodHelp(name)
        return wrappedAttr

    def _callWrapper(self, name, *args):
        if self.debug:
            print "%s(%s)" % (name, ", ".join([str(arg) for arg in args]))

        call = getattr(self.engine, name)
        resp = call(*args)

        if self.debug:
            print resp

        return resp

    @classmethod
    def newUUID(cls):
        return uuid.generate()

    def setURI(self, uri):
        self.uri = uri
        self.engine = xmlrpclib.ServerProxy(self.uri)

    def createStoragePool(self, *args):
        '''Execute 'createStoragePool' verb'''
        return self._callWrapper("createStoragePool", 0, *args)

    def createVolume(self, sdUUID, spUUID, imgUUID, size, volFormat,
        preallocate, diskType, volUUID, desc, srcImgUUID=BLANK_UUID,
        srcVolUUID=BLANK_UUID, blocked=True, timeout=300):

        '''Execute 'createVolume' verb'''

        resp = self.engine.createVolume(sdUUID, spUUID, imgUUID, size,
            volFormat, preallocate, diskType, volUUID, desc, srcImgUUID,
            srcVolUUID)
        if self.debug:
            print resp

        if resp['status']['code'] != 0:
            return resp

        if blocked:
            resp = self.waitForTask(resp['uuid'], timeout)

        return resp

    def deleteVolume(self, sdUUID, spUUID, imgUUID, volumes, postZero=False,
        force=False, blocked=True, timeout=60):
        '''Execute 'deleteVolume' verb'''

        resp = self.engine.deleteVolume(sdUUID, spUUID, imgUUID, volumes,
            postZero, force)
        if self.debug:
            print resp

        if resp['status']['code'] != 0:
            return resp

        if blocked:
            resp = self.waitForTask(resp['uuid'], timeout)

        return resp

    def mergeSnapshots(self, sdUUID, spUUID, vmUUID, imgUUID,
        ancestorUUID, successorUUID, postZero=False, blocked=True, timeout=300):
        '''Execute 'mergeSnapshots' verb'''

        resp = self.engine.mergeSnapshots(sdUUID, spUUID, vmUUID, imgUUID,
            ancestorUUID, successorUUID, postZero)

        if self.debug:
            print resp

        if resp['status']['code'] != 0:
            return resp

        if blocked:
            resp = self.waitForTask(resp['uuid'], timeout)

        return resp

    def deleteImage(self, sdUUID, spUUID, imgUUID, postZero=False, force=False,
        blocked=True, timeout=60):
        '''Execute 'deleteImage' verb'''

        resp = self.engine.deleteImage(sdUUID, spUUID, imgUUID, postZero, force)

        if self.debug:
            print resp

        if resp['status']['code'] != 0:
            return resp

        if blocked:
            resp = self.waitForTask(resp['uuid'], timeout)

        return resp

    def copyImage(self, sdUUID, spUUID, vmUUID, srcImgUUID, srcVolUUID,
        dstImgUUID, dstVolUUID, description='', dstSdUUID=BLANK_UUID,
        volType=SHARED_VOL, volFormat=UNKNOWN_VOL, preallocate=UNKNOWN_VOL,
        postZero=False, force=False, blocked=True, timeout=60):
        '''Execute 'copyImage' verb'''

        resp = self.engine.copyImage(sdUUID, spUUID, vmUUID,
            srcImgUUID, srcVolUUID, dstImgUUID, dstVolUUID, description,
            dstSdUUID, volType, volFormat, preallocate, postZero, force)

        if self.debug:
            print resp

        if resp['status']['code'] != 0:
            return resp

        if blocked:
            resp = self.waitForTask(resp['uuid'], timeout)

        return resp

    def moveImage(self, spUUID, srcDomUUID, dstDomUUID, imgUUID, vmUUID, op,
        postZero=False, force=False, blocked=True, timeout=600):
        '''Execute 'moveImage' verb'''

        resp = self.engine.moveImage(spUUID, srcDomUUID, dstDomUUID,
            imgUUID, vmUUID, op, postZero, force)

        if self.debug:
            print resp

        if resp['status']['code'] != 0:
            return resp

        if blocked:
            resp = self.waitForTask(resp['uuid'], timeout)

        return resp

    def waitForTask(self, taskID, timeout=-1):
        '''
        Wait for a specific task to complete.
        '''
        timeout = int(timeout)
        resp = self.getTaskStatus(taskID)

        #print "Task status", resp

        while timeout != 0 and resp['taskStatus']['taskState'] != 'finished':
            time.sleep(1)
            timeout -= 1
            resp = self.getTaskStatus(taskID)

        if resp['taskStatus']['taskState'] != 'finished':
            raise TaskNotFinished(resp)
        if resp['taskStatus']['taskResult'] != 'success':
            raise TaskFailed(resp)

        return resp

